using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Text;

namespace DiscoveryClient
{
    class Program
    {
        private const int ServerPort = 36669;
        private const string RequestMagic = "IPP_DISCOVERY_REQUEST";

        static void Main(string[] args)
        {
            Console.Title = "DiscoveryClient - UDP 发现客户端 (.NET Framework 4.6)";

            string targetMode = "broadcast";
            IPAddress directIp = null;
            if (args != null && args.Length > 0)
            {
                IPAddress ip;
                if (IPAddress.TryParse(args[0], out ip))
                {
                    directIp = ip;
                    targetMode = "unicast";
                }
            }

            if (targetMode == "unicast")
            {
                Console.WriteLine("[Client] 发送发现请求到指定服务器 {0}:{1} ...", directIp, ServerPort);
            }
            else
            {
                Console.WriteLine("[Client] 发送发现请求广播（按本机网卡计算广播地址）...");
            }

            UdpClient udp = null;
            try
            {
                udp = new UdpClient();
                udp.EnableBroadcast = true;

                byte[] requestBytes = Encoding.UTF8.GetBytes(RequestMagic);

                List<IPEndPoint> targets = new List<IPEndPoint>();

                if (targetMode == "unicast")
                {
                    targets.Add(new IPEndPoint(directIp, ServerPort));
                }
                else
                {
                    targets.AddRange(GetBroadcastEndpoints(ServerPort));

                    // 兜底：如果没算出任何广播地址，退回到 255.255.255.255
                    if (targets.Count == 0)
                    {
                        targets.Add(new IPEndPoint(IPAddress.Broadcast, ServerPort));
                    }
                }

                foreach (var targetEp in targets)
                {
                    udp.Send(requestBytes, requestBytes.Length, targetEp);
                    Console.WriteLine("[Client] 已向 {0} 发送请求: {1}", targetEp, RequestMagic);
                }

                udp.Client.ReceiveTimeout = 3000;

                List<string> responses = new List<string>();

                Console.WriteLine("[Client] 等待服务器响应（最多 3 秒）...");

                while (true)
                {
                    try
                    {
                        IPEndPoint remoteEp = new IPEndPoint(IPAddress.Any, 0);
                        byte[] data = udp.Receive(ref remoteEp);
                        string text = Encoding.UTF8.GetString(data ?? new byte[0]);

                        Console.WriteLine("[Client] 收到来自 {0} 的响应: {1}", remoteEp, text);
                        responses.Add(text);
                    }
                    catch (SocketException sex)
                    {
                        if (sex.SocketErrorCode == SocketError.TimedOut)
                        {
                            Console.WriteLine("[Client] 接收超时，结束等待。");
                            break;
                        }

                        Console.WriteLine("[Client] Socket 出错: " + sex);
                        break;
                    }
                }

                Console.WriteLine();
                Console.WriteLine("========== 解析结果 ==========");

                if (responses.Count == 0)
                {
                    Console.WriteLine("未发现任何服务器（可能是防火墙未放行 UDP 36669，或服务端未运行）。");
                }
                else
                {
                    foreach (var resp in responses)
                    {
                        ParseAndPrintResponse(resp);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("[Client] 出错: " + ex);
            }
            finally
            {
                if (udp != null)
                {
                    udp.Close();
                }
            }

            Console.WriteLine();
            Console.WriteLine("按任意键退出...");
            Console.ReadKey();
        }

        private static IEnumerable<IPEndPoint> GetBroadcastEndpoints(int port)
        {
            List<IPEndPoint> list = new List<IPEndPoint>();
            HashSet<string> seen = new HashSet<string>();

            try
            {
                NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
                foreach (var nic in nics)
                {
                    if (nic.OperationalStatus != OperationalStatus.Up)
                        continue;

                    if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
                        nic.NetworkInterfaceType == NetworkInterfaceType.Tunnel)
                        continue;

                    var ipProps = nic.GetIPProperties();
                    foreach (var ua in ipProps.UnicastAddresses)
                    {
                        if (ua.Address.AddressFamily != AddressFamily.InterNetwork)
                            continue;

                        if (ua.IPv4Mask == null)
                            continue;

                        byte[] ipBytes = ua.Address.GetAddressBytes();
                        byte[] maskBytes = ua.IPv4Mask.GetAddressBytes();
                        if (ipBytes.Length != 4 || maskBytes.Length != 4)
                            continue;

                        byte[] broadcastBytes = new byte[4];
                        for (int i = 0; i < 4; i++)
                        {
                            broadcastBytes[i] = (byte)(ipBytes[i] | (~maskBytes[i]));
                        }

                        string key = broadcastBytes[0] + "." + broadcastBytes[1] + "." + broadcastBytes[2] + "." + broadcastBytes[3];
                        if (seen.Add(key))
                        {
                            list.Add(new IPEndPoint(new IPAddress(broadcastBytes), port));
                        }
                    }
                }
            }
            catch
            {
            }

            return list;
        }

        private static void ParseAndPrintResponse(string resp)
        {
            if (string.IsNullOrEmpty(resp))
                return;

            if (!resp.StartsWith("IPP_SERVER|", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine("  非预期响应: " + resp);
                return;
            }

            string body = resp.Substring("IPP_SERVER|".Length);
            string[] parts = body.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            string serverName = "";
            string ip = "";
            string port = "";
            string printersRaw = "";

            foreach (var part in parts)
            {
                int idx = part.IndexOf('=');
                if (idx <= 0) continue;

                string key = part.Substring(0, idx);
                string value = part.Substring(idx + 1);

                switch (key)
                {
                    case "Name":
                        serverName = value;
                        break;
                    case "IP":
                        ip = value;
                        break;
                    case "Port":
                        port = value;
                        break;
                    case "Printers":
                        printersRaw = value;
                        break;
                }
            }

            Console.WriteLine("服务器: {0} ({1}:{2})", serverName, ip, port);

            if (!string.IsNullOrEmpty(printersRaw))
            {
                string[] printerItems = printersRaw.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var item in printerItems)
                {
                    string[] kv = item.Split(new[] { ',' }, 2);
                    string pName = kv.Length > 0 ? kv[0] : "";
                    string pUri = kv.Length > 1 ? kv[1] : "";
                    Console.WriteLine("  - 打印机: {0}", pName);
                    Console.WriteLine("    URI  : {0}", pUri);
                }
            }
        }
    }
}


